/* $Id: SearchPerformer.java,v 1.2 2003/08/02 20:57:54 survey Exp $
 * 
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.medbeans.modules.search;


import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.openide.actions.FindAction;
import org.openide.nodes.Node;
import org.openide.util.actions.ActionPerformer;
import org.openide.util.actions.SystemAction;
import org.openide.util.Lookup;
//import org.openide.util.Task;
import org.openide.windows.TopComponent;
import org.openidex.search.SearchGroup;
import org.openidex.search.SearchType;


/**
 * <code>FindAction</code> performer providing search dialog.
 * This classes is hooked on search action.
 *
 * @author  Petr Kuzel
 * @see SearchHook
 * @see org.openide.actions.FindAction
 */
public class SearchPerformer implements ActionPerformer {
    
    /** Cached top component via soft reference. */
    private static SoftReference resultViewSRef = new SoftReference(null);
    
    /** Singleton instance of performer. */
    private static SearchPerformer searchPerformer;

    /** Lookup result for getting all search type instances. */
    private Lookup.Result result;
    

    /** Creates new <code>SearchPerformer</code>. */
    private SearchPerformer() {}


    /** Gets <code>SearchPerformer</code> singleton instance. */
    static synchronized SearchPerformer getDefault() {
        if(searchPerformer == null)
            searchPerformer = new SearchPerformer();
        
        return searchPerformer;
    }
    

    /** Gets lookup result holding script type instances. */
    private Lookup.Result getSearchTypes() {
        if(result == null) {
            result = Lookup.getDefault().lookup(new Lookup.Template(SearchType.class));
        }
        
        return result;
    }
    
    
    /**
     * Gets set of search types for provided nodes.
     * @param nodes currently selected nodes
     * @return set of <code>SearchType</code> classes that are enabled upon currently
     * selected nodes.
     */
    private List getTypes(Node[] nodes) {
        List searchTypeList = new ArrayList(5);

        if(nodes == null || nodes.length == 0) {
            return searchTypeList;
        }

        // Clone the origianls and test them if are enabled on specifies nodes.
        Iterator it = getSearchTypes().allInstances().iterator();
        while(it.hasNext()) {
            SearchType searchType = (SearchType)it.next();

            if(searchType.enabled(nodes) && !searchTypeList.contains(searchType)) {
                searchTypeList.add(searchType);
            }
        }

        return searchTypeList;
    }

    /** Tests if performer is enabled based upon provided nodes.
     * @param nodes currently selected nodes
     * @return <code>true</code> if some search type is enabled */
    public boolean enabled(Node[] nodes) {
        Iterator it = getSearchTypes().allInstances().iterator();
        
        while(it.hasNext()) {
            SearchType searchType = (SearchType)it.next();

            if(searchType.enabled(nodes)) {
                return true;
            }
        }

        return false;
    }

    /** Performs search on nodes. Displays <code>CriteriaView</code> with predefined search types set.
     * @param nodes currently selected nodes
     * @see CriteriaView
     */
    private void performAction(Node[] nodes) {
        List searchTypeList = getTypes(nodes);
        
        if(searchTypeList.isEmpty()) {
            return;
        }
        
        // Clone list of search types.
        List clonedSearchTypeList = new ArrayList(searchTypeList.size());
        
        for(Iterator it = searchTypeList.iterator(); it.hasNext(); ) {
            clonedSearchTypeList.add(((SearchType)it.next()).clone());
        }
        
        SearchPanel searchPanel = new SearchPanel(clonedSearchTypeList);

        searchPanel.showDialog();

        if(searchPanel.getReturnStatus() == SearchPanel.RET_OK) {

            ResultModel resultModel = performSearch(nodes, searchPanel, clonedSearchTypeList);

            ResultViewTopComponent resultWindow = (ResultViewTopComponent)resultViewSRef.get();
            
            if(resultWindow == null) {// create a new result window
                resultWindow = new ResultViewTopComponent(resultModel);
                resultViewSRef = new SoftReference(resultWindow);
            } else { // reuse the result window, only set the new result model
                resultWindow.setModel(resultModel);
            }

            resultWindow.open();
            resultWindow.requestFocus();
        }
    }

    /** Performs actual search.
     * @param nodes nodes to search
     * @param criteriaModel model to use */
    private ResultModel performSearch(Node[] nodes, SearchPanel searchPanel, List searchTypeList) {
        SearchEngine searchEngine = new SearchEngine();
        
        SearchGroup[] groups = SearchGroup.createSearchGroups(searchPanel.getCustomizedSearchTypes());
        
        SearchGroup searchGroup = null;
        
        if(groups.length > 0) {
            // PENDING Here should be solved cases when more groups were created,
            // if not only intersection result is necessary etc.
            searchGroup = groups[0];            
        }
        
        ResultModel resultModel = new ResultModel(searchTypeList, searchGroup);

        SearchTask task = searchEngine.search(nodes, searchGroup, resultModel);

        resultModel.setTask(task);

        return resultModel;
    }

    /** Implements <code>ActionPerformer</code>. Makes search support moving. Asks SearchEngine for search task. 
     * Runs it. Collects search results to <code>ResultModel</code> and displays it.
     * @see ResultModel */
    public void performAction(SystemAction action) {
        if(action instanceof RepositorySearchAction ) {
            RepositorySearchAction repositoryAction = (RepositorySearchAction)action;
            performAction(repositoryAction.getNodes());

        } else if(action instanceof FindAction) {
            performAction(TopComponent.getRegistry().getCurrentNodes());

        } else {
            // unexpected action
            if(Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
                throw new RuntimeException("Should not occure."); // NOI18N
        }
    }
    
}
